package com.example.springapplicationdemo.errorcontroller;

import com.example.springapplicationdemo.vo.RestResVo;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * 自定义ErrorAttribute会替换Spring Boot 默认实现，以返回与RestRes兼容的Map数据
 */
@Component
public class RestResVoErrorAttributes implements ErrorAttributes, HandlerExceptionResolver {
    private static final String EXCEPTION_KEY = RestResVoErrorAttributes.class.getName() + ".ERROR";

    /**
     * 将异常信息转化为与RestResVO兼容的Map结果
     * @param webRequest
     * @param includeStackTrace
     * @return
     */
    @Override
    public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
        Throwable throwable = getError(webRequest);
        RestResVo restResVo = RestResVo.error(throwable);
        return restResVo.toMapData();
    }


    /**
     * 从Request上下文获取异常信息
     * @param webRequest
     * @return
     */
    @Override
    public Throwable getError(WebRequest webRequest) {
        Throwable exception = getAttribute(webRequest, EXCEPTION_KEY);
        if (exception == null) {
            exception = getAttribute(webRequest, "javax.servlet.error.exception");
        }
        return exception;
    }

    /**
     * 把异常信息绑定到request上下文，以便稍后使用
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @return
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        storeErrorAttributes(request, ex);
        return null;
    }

    private void storeErrorAttributes(HttpServletRequest request, Exception ex) {
        request.setAttribute(EXCEPTION_KEY, ex);
    }

    @SuppressWarnings("unchecked")
    private <T> T getAttribute(RequestAttributes requestAttributes, String name) {
        return (T) requestAttributes.getAttribute(name, RequestAttributes.SCOPE_REQUEST);
    }
}
